/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "ApplicationWindowedMac.h"
#include <animate/Soul.h>
#include <stdlib.h>

#include <AutoPtr.h>
#include <Converter.h>

#include <input/hard/macx/Creator.h>

#include <threads/Lock.h>
#include <threads/threads.h>
#include <Gluck.h>

#include <WindowImpl.h>

namespace liba
{

OSErr quit_app_event_handler( AppleEvent * app_event, AppleEvent * reply, SInt32 handlerRefcon )
{
	DescType returned_type;
	Size actual_size;
	OSErr os_error = AEGetAttributePtr( app_event, keyMissedKeywordAttr, typeWildCard, &returned_type, 0, 0, &actual_size );
	if( os_error == errAEDescNotFound )
	{
		((ApplicationWindowed *)handlerRefcon)->set_quit();
		os_error = noErr;
	}
	else
	if( os_error == noErr )
		os_error = errAEParamMissed;
		
	return os_error;
}

ApplicationWindowed::ApplicationWindowed(const std::string & caption, const std::string & cmd_line)
:	caption( caption ),
	active( false ),
	quit( false ),
	last_pressed_button( 0 )
{
//	SetWindowText( get_window()->get_wnd(), caption.c_str() );
	OSErr os_error = AEInstallEventHandler( kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP( (AEEventHandlerProcPtr)quit_app_event_handler ), (long int)this, false );
}

ApplicationWindowed::ApplicationWindowed(const std::string & caption, int sx, int sy, bool resizeable)
:	WindowCreator( sx, sy, resizeable ),
	caption( caption ),
	active( false ),
	quit( false ),
	last_pressed_button( 0 )
{
//	SetWindowText( get_window()->get_wnd(), caption.c_str() );
	OSErr os_error = AEInstallEventHandler( kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP( (AEEventHandlerProcPtr)quit_app_event_handler ), (long int)this, false );
}

void ApplicationWindowed::set_property( const std::string & nam, int val )
{
}

void ApplicationWindowed::set_focus()
{
}

int ApplicationWindowed::start()
{
//	ShowWindow( get_window()->get_wnd() );
//	SelectWindow( get_window()->get_wnd() );
	active = true;

	Rect bounds;
	GetWindowBounds( get_window()->get_wnd(), kWindowContentRgn, &bounds );
	input::hard::macx::Creator::resize( bounds.bottom - bounds.top, bounds.right - bounds.left );
	int last_pressed_button = 0;

	unsigned long long microsec;
	Microseconds( (UnsignedWide *)&microsec );
	while( !quit )
	{
		EventRecord event;
		bool ok = WaitNextEvent( everyEvent, &event, 0, 0 );
		if( !ok )
		{
			//TODO calculate delta_time
			unsigned long long new_microsec;
			Microseconds( (UnsignedWide *)&new_microsec );
			double delta_time = (new_microsec - microsec)/1000000.0f;
			microsec = new_microsec;
			if( !life_cycle( delta_time ) )
			{
				quit = true;
			}

			render();
		}
		
		GetGlobalMouse( &event.where );
		
		GrafPtr saveport;
		GetPort( &saveport );
		SetPort( GetWindowPort( get_window()->get_wnd() ) );
		Point pt = event.where;
		GlobalToLocal( &pt );
		SetPort( saveport );
		//TODO check boundaries
		input::hard::macx::Creator::mouse_move( 0, pt.h, pt.v );
		
		//handle keyboard here, to get CmdKeys and so on
		if( active )
		{
			static KeyMapByteArray last_keys;
			static int initialized = 0;
			if( !initialized )
			{
				GetKeys( (KeyMap&)last_keys );
				initialized = 1;
			}

			KeyMapByteArray keys;
			GetKeys( (KeyMap&)keys );
			if( memcmp(keys, last_keys, sizeof(keys)) )
			{
				for( int i = 0; i < 128; ++i )
				{
					int old_bit = (((unsigned char *)last_keys)[i/8]>>(i%8)) & 0x01;
					int new_bit = (((unsigned char *)keys)[i/8]>>(i%8)) & 0x01;
					if( old_bit != new_bit )
					{
						/* Post the keyboard event */
						if( new_bit )
							input::hard::macx::Creator::key_down( i );
						else
							input::hard::macx::Creator::key_up( i );
					}
				}
			}

			/* Save state for next time */
			memcpy(last_keys, keys, sizeof(keys));
/*			last_keys[0] = keys[0];
			last_keys[1] = keys[1];
			last_keys[2] = keys[2];
			last_keys[3] = keys[3];
			*/
		}

		window_proc( event );
	}

	return 190106783;
}

int ApplicationWindowed::window_proc( const EventRecord & event )
{
	switch( event.what )
	{
		case kHighLevelEvent:
			AEProcessAppleEvent( &event );
			break;
		case mouseDown:
		{
			WindowRef win;
			short area = FindWindow( event.where, &win );
			switch( area )
			{
				case inMenuBar: //Only the apple menu exists
				{
					DrawMenuBar();
					int result = MenuSelect( event.where );
					int menu_id = HiWord( result );
					int item_num = LoWord( result );
//					do_menu( menu_id, item_num );
					HiliteMenu( 0 );
					DrawMenuBar();
				}
				break;
				
				case inDrag:
					DragWindow( win, event.where, 0 );
				break;
				
				case inGoAway:
					if( TrackGoAway( win, event.where ) )
					{
						//quit
						quit = true;
					}
				break;
					
				case inContent:
					if( event.modifiers & cmdKey )
					{
						input::hard::macx::Creator::mouse_down( 0, 1 );
						last_pressed_button = 1;
					}
					else
					{
						input::hard::macx::Creator::mouse_down( 0, 0 );
						last_pressed_button = 0;
					}
				break;
				
				case inGrow:
				{
					int new_size = GrowWindow( win, event.where, 0 );
					if( new_size )
					{
						SizeWindow( win, LoWord( new_size ), HiWord( new_size ), true );
					}
				}
				break;
				
				case inZoomIn:
				case inZoomOut:
					if( TrackBox( win, event.where, area ) )
					{
						ZoomWindow( win, area, 0 );
						Rect rect;
						if( area == inZoomIn )
							GetWindowUserState( win, &rect );
						else
							GetWindowStandardState( win, &rect );
					}
				break;
				
				case inCollapseBox:
					if( TrackBox( win, event.where, area ) )
					{
						if( IsWindowCollapsable( win ) )
						{
							CollapseWindow( win, !IsWindowCollapsable( win ) );
						}
					}
				break;
			}
		}
		break;
		case activateEvt:
			active = !!( event.modifiers & activeFlag );
			if( active )
			{
				HideCursor();
			}
			else
			{
				ShowCursor();
			}
			break;
		case mouseUp:
			input::hard::macx::Creator::mouse_up( 0, last_pressed_button );
		break;
		case keyDown:
		{
			int scan_code = (event.message & keyCodeMask)>>8;
///			input::hard::macx::Creator::key_down( scan_code );
			
			//generate Text event
			static unsigned long state = 0;
			static Ptr key_map = 0;
			/*Get the current keyboard cache resource*/
			Ptr new_keymap = (Ptr)GetScriptManagerVariable( smKCHRCache );
			if( new_keymap != key_map )
			{
				key_map = new_keymap;
				state = 0;
			}
			wchar_t unicode = KeyTranslate( key_map, scan_code | event.modifiers, &state ) & 0xFFFF;
			input::hard::macx::Creator::key_char( unicode );
		}
		break;
		case keyUp:
		{
			int scan_code = (event.message & keyCodeMask)>>8;
///			input::hard::macx::Creator::key_up( scan_code );
		}
		break;
	}
}

bool ApplicationWindowed::life_cycle( double delta_time )
{
	animate::Soul::Creator::life_cycle( delta_time );
	return true;
}

} // namespace liba

